<template>
  <div id="container">
    <!-- 进度条 -->
    <div id="per"> </div>
  </div>
</template>

<script setup>
import * as THREE from 'three'
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { onMounted } from 'vue';
// 引入gltf模型加载库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 实例化一个gui对象
const gui = new GUI();
//改变交互界面style属性
gui.domElement.style.right = '0px';
gui.domElement.style.width = '300px';
// 创建场景
const scene = new THREE.Scene();
const width = window.innerWidth - 200;
const height = window.innerHeight - 60;

// 创建渲染器
const renderer = new THREE.WebGLRenderer({
  //想把canvas画布上内容下载到本地，需要设置为true
  preserveDrawingBuffer: true,
  // 设置对数深度缓冲区，优化深度冲突问题
  // // 两个矩形平面Mesh重合，产生闪烁
  // 闪烁原因：两个矩形面位置重合，GPU无法分清谁在前谁在后
  // 在三维软件中建模的的时候，尽量避免两个Mesh完全重合，可以考虑适当偏移一定的距离
  // 当一个三维场景中有一些面距离比较近，有深度冲突，你可以尝试设置webgl渲染器设置对数深度缓冲区logarithmicDepthBuffer: true来优化或解决。logarithmicDepthBuffer: true作用简单来说，就是两个面间距比较小的时候，让threejs更容易区分两个面，谁在前，谁在后。

  logarithmicDepthBuffer: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
// 创建GLTF加载器对象
const loader = new GLTFLoader();
loader.load(`static/mesh.glb`, function (gltf) {
  scene.add(gltf.scene);
}, function (xhr) {
  const percent = xhr.loaded / xhr.total;
  console.log('加载进度' + percent);
})
onMounted(() => {
  const percentDiv = document.getElementById("per");// 获取进度条元素
  percentDiv.style.width = 0.8 * 400 + "px";//进度条元素长度
  percentDiv.style.textIndent = 0.8 * 400 + 5 + "px";//缩进元素中的首行文本
  percentDiv.innerHTML = "80%";//进度百分比

  // // 创建GLTF加载器对象
  // const loader = new GLTFLoader();
  // loader.load(`static/mesh.glb`, function (gltf) {
  //   scene.add(gltf.scene);
  // }, function (xhr) {
  //   console.log('xhr', xhr)
  //   const percent = xhr.loaded / xhr.total;
  //   // console.log('加载进度' + percent);
  //   percentDiv.style.width = percent * 400 + "px"; //进度条元素长度
  //   percentDiv.style.textIndent = percent * 400 + 5 + "px"; //缩进元素中的首行文本
  //   // Math.floor:小数加载进度取整
  //   percentDiv.innerHTML = Math.floor(percent * 100) + '%'; //进度百分比
  // })
})




// 添加一个辅助网格地面
const gridHelper = new THREE.GridHelper(10, 10, 0x004444, 0x004444);
scene.add(gridHelper)

// AxesHelper：辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(20);
scene.add(axesHelper);
// 实例化一个透视投影相机对象
// 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000：远裁截面
// // 超出视锥体远裁界面的范围的会被剪裁掉，不渲染  可以调整far参数适配 所以要看全部就把远裁面拉高10000
// const camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000);
// 200表示左侧div元素宽度195px+间距5px
const width2 = window.innerWidth - 200; //canvas画布高度
//60表示顶部div元素高度55px+间距5px
const height2 = window.innerHeight - 60; //canvas画布宽度
const camera = new THREE.PerspectiveCamera(30, width2 / height2, 1, 3000);
renderer.setSize(width2, height2);
//相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(100, 100, 100);
// camera.lookAt(mesh.position);//指向mesh对应的位置
// // 改变相机观察目标点
camera.lookAt(10, 0, 0);

// 平行光
// const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
// // 设置光源的方向：通过光源position属性和目标指向对象的position属性计算
// directionalLight.position.set(80, 100, 50);
// // 方向光指向对象网格模型mesh，可以不设置，默认的位置是0,0,0
// directionalLight.target = mesh;
// scene.add(directionalLight);
// // DirectionalLightHelper：可视化平行光
// const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0000);
// scene.add(dirLightHelper);

// 不同硬件设备的屏幕的设备像素比window.devicePixelRatio值可能不同
console.log('查看当前屏幕设备像素比', window.devicePixelRatio);
// 或者直接设置参数  renderer.antialias = true

// 如果你遇到你的canvas画布输出模糊问题，注意设置renderer.setPixelRatio(window.devicePixelRatio)。
// 注意：注意你的硬件设备设备像素比window.devicePixelRatio刚好是1，那么是否执行.setPixelRatio()不会有明显差异，不过为了适应不同的硬件设备屏幕，通常需要执行该方法。
// 获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);

renderer.setClearColor(0x444444, 1); //设置背景颜色

function render() {
  // console.log('camera', camera.position)
  renderer.render(scene, camera); //执行渲染操作
  requestAnimationFrame(render);//请求再次执行渲染函数render，渲染下一帧
}
render();

const controls = new OrbitControls(camera, renderer.domElement);
// 相机控件.target属性在OrbitControls.js内部表示相机目标观察点，默认0,0,0
// console.log('controls.target', controls.target);
controls.target.set(10, 0, 0);
controls.update();//update()函数内会执行camera.lookAt(controls.targe)


// 如果OrbitControls改变了相机参数，重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
  // 浏览器控制台查看相机位置变化
  // console.log('camera.position', camera.position);
  // 渲染循环和相机控件OrbitControls

  // 设置了渲染循环, 相机控件OrbitControls就不用再通过事件change执行renderer.render(scene, camera);，毕竟渲染循环一直在执行renderer.render(scene, camera);。
  // renderer.render(scene, camera); //执行渲染操作
});//监听鼠标、键盘事件
// onresize 事件会在窗口被调整大小时发生
// window.onresize = function () {
//   // 重置渲染器输出画布canvas尺寸
//   renderer.setSize(window.innerWidth, window.innerHeight);
//   // 全屏情况下：设置观察范围长宽比aspect为窗口宽高比
//   camera.aspect = window.innerWidth / window.innerHeight;
//   // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
//   // 但是不会每渲染一帧，就通过相机的属性计算投影矩阵(节约计算资源)
//   // 如果相机的一些属性发生了变化，需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
//   camera.updateProjectionMatrix();
// };
// 画布跟随窗口变化
window.onresize = function () {
  const width = window.innerWidth - 200; //canvas画布高度
  const height = window.innerHeight - 60; //canvas画布宽度
  renderer.setSize(width, height);
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
};

const saveFile = () => {
  // 创建一个超链接元素，用来下载保存数据的文件
  const link = document.createElement('a');
  // 通过超链接herf属性，设置要保存到文件中的数据

  // Canvas画布通过.toDataURL()方法可以获取画布上的像素信息。canvas.toDataURL("image/png");表示以png格式获取像素数据，可以直接赋值给超链接元素a的.herf属性下载到本地。

  const canvas = renderer.domElement; //获取canvas对象
  link.href = canvas.toDataURL("image/png");
  // link.href = window.URL.createObjectURL(new Blob(['一些数据']));

  link.download = 'threejs.png';//下载文件名
  link.click();//js代码触发超链接元素a的鼠标点击事件，开始下载文件到本地
}
</script>
<style scoped>
/* 进度条css样式 */
#container {
  position: absolute;
  width: 400px;
  height: 16px;
  top: 50%;
  left: 50%;
  margin-left: -200px;
  margin-top: -8px;
  border-radius: 8px;
  border: 1px solid #009999;
  overflow: hidden;
}

#per {
  height: 100%;
  width: 0px;
  background: #00ffff;
  color: #00ffff;
  line-height: 15px;
}
</style>